﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.ApplicationInsights.Channel;
using IChannelTelemetry = Microsoft.ApplicationInsights.Channel.ITelemetry;

namespace Microsoft.DotNet.Cli.Telemetry.PersistenceChannel;

/// <summary>
///     Represents a communication channel for sending telemetry to Application Insights via HTTPS.
/// </summary>
internal sealed class PersistenceChannel : ITelemetryChannel
{
    internal const string TelemetryServiceEndpoint = "https://dc.services.visualstudio.com/v2/track";

    private readonly FlushManager _flushManager;

    private int _disposeCount;
    private readonly BaseStorageService _storage;
    private readonly PersistenceTransmitter _transmitter;

    /// <summary>
    ///     Initializes a new instance of the <see cref="PersistenceChannel" /> class.
    /// </summary>
    /// <param name="storageDirectoryPath">
    ///     Full path of a directory name. Under this folder all the transmissions will be saved.
    ///     Setting this value groups channels, even from different processes.
    ///     If 2 (or more) channels has the same <c>storageFolderName</c> only one channel will perform the sending even if the
    ///     channel is in a different process/AppDomain/Thread.
    /// </param>
    /// <param name="sendersCount">
    ///     Defines the number of senders. A sender is a long-running thread that sends telemetry batches in intervals defined
    ///     by <see cref="SendingInterval" />.
    ///     So the amount of senders also defined the maximum amount of http channels opened at the same time.
    /// </param>
    public PersistenceChannel(string storageDirectoryPath = null, int sendersCount = 1)
    {
        _storage = new StorageService();
        _storage.Init(storageDirectoryPath);
        _transmitter = new PersistenceTransmitter(_storage, sendersCount);
        _flushManager = new FlushManager(_storage);
        EndpointAddress = TelemetryServiceEndpoint;
    }

    /// <summary>
    ///     Gets or sets an interval between each successful sending.
    /// </summary>
    /// <remarks>
    ///     On error scenario this value is ignored and the interval will be defined using an exponential back-off
    ///     algorithm.
    /// </remarks>
    public TimeSpan? SendingInterval
    {
        get => _transmitter.SendingInterval;
        set => _transmitter.SendingInterval = value;
    }


    /// <summary>
    ///     Gets or sets the maximum amount of files allowed in storage. When the limit is reached telemetries will be dropped.
    /// </summary>
    public uint MaxTransmissionStorageFilesCapacity
    {
        get => _storage.MaxFiles;
        set => _storage.MaxFiles = value;
    }

    /// <summary>
    ///     This flag has no effect. But it is required by base class
    /// </summary>
    public bool? DeveloperMode { get; set; }

    /// <summary>
    ///     Gets or sets the HTTP address where the telemetry is sent.
    /// </summary>
    public string EndpointAddress
    {
        get => _flushManager.EndpointAddress.ToString();

        set
        {
            string address = value ?? TelemetryServiceEndpoint;
            _flushManager.EndpointAddress = new Uri(address);
        }
    }

    /// <summary>
    ///     Releases unmanaged and - optionally - managed resources.
    /// </summary>
    public void Dispose()
    {
        if (Interlocked.Increment(ref _disposeCount) == 1)
        {
            _transmitter?.Dispose();
        }
    }

    /// <summary>
    ///     Sends an instance of ITelemetry through the channel.
    /// </summary>
    public void Send(IChannelTelemetry item)
    {
        _flushManager.Flush(item);
    }

    /// <summary>
    ///     No operation, send will always flush. So nothing will be in memory
    /// </summary>
    public void Flush()
    {
    }
}
